package bitmap;

import java.io.IOException;

import btree.ConstructPageException;
import diskmgr.*;
import global.*;
import heap.*;

interface ConstSlot {
	int INVALID_SLOT = -1;
	int EMPTY_SLOT = -1;
}

public class BMPage extends Page implements GlobalConst, ConstSlot {
	public static final int SIZE_OF_SLOT = 1;//modified from 4 to 1
	public static final int DPFIXED = 4 * 2 + 3 * 4;

	public static final int SLOT_CNT = 0;
	public static final int USED_PTR = 2;
	public static final int FREE_SPACE = 4;
	public static final int TYPE = 6;
	public static final int PREV_PAGE = 8;
	public static final int NEXT_PAGE = 12;
	public static final int CUR_PAGE = 16;

	/**
	 * number of slots in use
	 */
	protected short slotCnt;

	/**
	 * offset of first used byte by data records in data[]
	 */
	protected short usedPtr;

	/**
	 * number of bytes free in data[]
	 */
	protected short freeSpace;

	/**
	 * an arbitrary value used by subclasses as needed
	 */
	protected short type;

	/**
	 * backward pointer to data page
	 */
	protected PageId prevPage = new PageId();

	/**
	 * forward pointer to data page
	 */
	protected PageId nextPage = new PageId();

	/**
	 * page number of this page
	 */
	protected PageId curPage = new PageId();

	/**
	 * Default constructor
	 * @throws ConstructPageException 
	 */

	public BMPage() throws ConstructPageException {
		super();
		try {
			Page apage = new Page();
			PageId pageId = SystemDefs.JavabaseBM.newPage(apage, 1);
			if (pageId == null)
				throw new ConstructPageException(null, "new page failed");
			this.init(pageId, apage);
                        SystemDefs.JavabaseBM.unpinPage(pageId, false);

		} catch (Exception e) {
			throw new ConstructPageException(e, "construct header page failed");
		}
	}

	/**
	 * Constructor of class HFPage open a HFPage and make this HFpage piont to
	 * the given page
	 * 
	 * @param page
	 *            the given page in Page type
	 */

	public BMPage(Page page) {
		data = page.getpage();
	}

	// return the amount of available space on the page
	public int available_space() throws IOException {
		freeSpace = Convert.getShortValue(FREE_SPACE, data);
		return freeSpace;
	}

	// dump contents of a page
	public void dumpPage() throws IOException {
		int i, n;
		int length, offset;

		curPage.pid = Convert.getIntValue(CUR_PAGE, data);
		nextPage.pid = Convert.getIntValue(NEXT_PAGE, data);
		usedPtr = Convert.getShortValue(USED_PTR, data);
		freeSpace = Convert.getShortValue(FREE_SPACE, data);
		slotCnt = Convert.getShortValue(SLOT_CNT, data);

		System.out.println("dumpPage");
		System.out.println("curPage= " + curPage.pid);
		System.out.println("nextPage= " + nextPage.pid);
		System.out.println("usedPtr= " + usedPtr);
		System.out.println("freeSpace= " + freeSpace);
		System.out.println("slotCnt= " + slotCnt);

		for (i = 0, n = DPFIXED; i < slotCnt; n += SIZE_OF_SLOT, i++) {
			length = Convert.getShortValue(n, data);
			offset = Convert.getShortValue(n + 2, data);
			System.out.println("slotNo " + i + " offset= " + offset);
			System.out.println("slotNo " + i + " length= " + length);
		}

	}

	public short getSlotLength(int slotno) throws IOException {
		int position = DPFIXED + slotno * SIZE_OF_SLOT;
		short val = Convert.getShortValue(position, data);
		return val;
	}

	// determining if the page is empty
	public boolean empty() throws IOException {
		int i;
		short length;
		// look for an empty slot
		slotCnt = Convert.getShortValue(SLOT_CNT, data);

		for (i = 0; i < slotCnt; i++) {
			length = getSlotLength(i);
			if (length != EMPTY_SLOT)
				return false;
		}

		return true;
	}

	// constructor of class BMPage initialize a new page
	public void init(PageId pageNo, Page apage) throws IOException {
		data = apage.getpage();

		slotCnt = 0; // no slots in use
		Convert.setShortValue(slotCnt, SLOT_CNT, data);

		curPage.pid = pageNo.pid;
		Convert.setIntValue(curPage.pid, CUR_PAGE, data);

		nextPage.pid = prevPage.pid = INVALID_PAGE;
		Convert.setIntValue(prevPage.pid, PREV_PAGE, data);
		Convert.setIntValue(nextPage.pid, NEXT_PAGE, data);

		usedPtr = (short) DPFIXED; // offset in data array (grow forwards)
		Convert.setShortValue(usedPtr, USED_PTR, data);

		freeSpace = (short) (MAX_SPACE - DPFIXED); // amount of space available
		Convert.setShortValue(freeSpace, FREE_SPACE, data);
	}

	// constructor of class BMPage open a existed BMPage
	public void openBMpage(Page apage) {
		data = apage.getpage();
	}

	public PageId getCurPage() throws IOException {
	    curPage.pid =  Convert.getIntValue (CUR_PAGE, data);
	    return curPage;
	}

	public PageId getNextPage() throws IOException {
	    nextPage.pid =  Convert.getIntValue (NEXT_PAGE, data);    
	    return nextPage;
	}

	public PageId getPrevPage() throws IOException {
	    prevPage.pid =  Convert.getIntValue (PREV_PAGE, data);
	    return prevPage;
	}

	// set value of curPage to pageNo
	public void setCurPage(PageId pageNo) throws IOException {
	    curPage.pid = pageNo.pid;
	    Convert.setIntValue (curPage.pid, CUR_PAGE, data);
	}

	// set value of nextPage to pageNo
	public void setNextPage(PageId pageNo) throws IOException {
		nextPage.pid = pageNo.pid;
	    Convert.setIntValue (nextPage.pid, NEXT_PAGE, data);
	}

	// set value of prevPage to pageNo
	public void setPrevPage(PageId pageNo) throws IOException {
		prevPage.pid = pageNo.pid;
	    Convert.setIntValue (prevPage.pid, PREV_PAGE, data);
	}

	public byte[] getBMpageArray() throws IOException {		
	    byte[] result = new byte[(MAX_SPACE - DPFIXED - this.available_space())];
		
		System.arraycopy(this.data, DPFIXED, result, 0, (MAX_SPACE - DPFIXED - this.available_space()));
		
		return result;
	}

	public void writeBMPageArray(byte[] BMdata) throws IOException {	
		System.arraycopy(BMdata, 0, this.data, DPFIXED, BMdata.length);
		
		slotCnt = (short) BMdata.length;
		Convert.setShortValue(slotCnt, SLOT_CNT, data);
		
		freeSpace = (short) (MAX_SPACE - DPFIXED - BMdata.length);
		Convert.setShortValue(freeSpace, FREE_SPACE, data);
	}
}
